// Created on 15.07.2005 at 16:02
// Created by Mikhail Sokolov (mikhail.sokolov@smekalka.ru)
// Copyright (C) 2005 Smekalka! (http://smekalka.net)
// All rights reserved.

using System;
using System.Collections;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;

namespace FileHashCodeCalculator
{
	/// <summary>
	/// Represents a hash code value.
	/// </summary>
	public struct HashCode : IFormattable
	{
		private byte[] _value;

		public static readonly HashCode Empty = new HashCode((byte[]) null);

		private static readonly string EmptyValueString = "HashCode.Empty";

		public HashCode(byte[] hashCode)
		{
			_value = null;

			if (hashCode == null || hashCode.Length == 0) return;
			_value = hashCode;
		}

		public HashCode(string hashCode)
		{
			_value = null;

			Regex regex = new Regex("[0-9a-f]{1,2}", RegexOptions.IgnoreCase);
			MatchCollection matches = regex.Matches(hashCode);
			_value = new byte[matches.Count];
			for (int i = 0; i < matches.Count; i++) _value[i] = Byte.Parse(matches[i].Value, NumberStyles.HexNumber);
		}

		public int Size
		{
			get
			{
				if (_value == null) return 0;
				return _value.Length;
			}
		}

		public static implicit operator HashCode(byte[] hashCode)
		{
			return new HashCode(hashCode);
		}

		public static implicit operator byte[](HashCode hashCode)
		{
			return hashCode.ToByteArray();
		}

		public static implicit operator HashCode(string hashCode)
		{
			return new HashCode(hashCode);
		}

		public static implicit operator string(HashCode hashCode)
		{
			return hashCode.ToString();
		}

		public byte[] ToByteArray()
		{
			if (_value == null) return null;
			return (byte[]) _value.Clone();
		}

		public BitArray ToBitArray()
		{
			if (_value == null) return null;
			return new BitArray(_value);
		}

		public bool Equals(byte[] hashCode)
		{
			if (hashCode == null) return (_value == null);

			if (_value.Length != hashCode.Length) return false;

			for (int i = 0; i < _value.Length; i++)
			{
				if (_value[i] != hashCode[i]) return false;
			}
			return true;
		}

		public bool Equals(HashCode hashCode)
		{
			return Equals(hashCode._value);
		}

		public override bool Equals(object obj)
		{
			if (obj == null) return false;

			if (obj is HashCode) return Equals((HashCode) obj);
			else if	(obj is byte[]) return Equals((HashCode) obj);
			else return false;
		}

		public static bool operator ==(HashCode h1, HashCode h2)
		{
			return h1.Equals(h2);
		}

		public static bool operator !=(HashCode h1, HashCode h2)
		{
			return !h1.Equals(h2);
		}

		public override int GetHashCode()
		{
			return _value.GetHashCode();
		}

		private string[] GetByteStrings(IFormatProvider formatProvider)
		{
			if (_value == null || _value.Length == 0) return null;

			string[] byteStrings = new string[_value.Length];

			for (int i = 0; i < _value.Length; i++)
			{
				byteStrings[i] = _value[i].ToString("X2", formatProvider);
			}

			return byteStrings;
		}

		public string ToString(string format, IFormatProvider formatProvider)
		{
			if (_value == null || _value.Length == 0) return EmptyValueString;

			string separator;
			string finalFormat = null;

			if (format == "D" || format == null || format == String.Empty)
			{
				separator = "-";
			}
			else if (format == "N")
			{
				separator = String.Empty;
			}
			else if (format == "B")
			{
				separator = "-";
				finalFormat = "{{0}}";
			}
			else if (format == "P")
			{
				separator = "-";
				finalFormat = "({0})";
			}
			else
				throw (new FormatException());

			string _string = String.Join(separator, GetByteStrings(formatProvider));

			if (finalFormat == null) return _string;
			else return String.Format(finalFormat, _string);
		}

		public override string ToString()
		{
			if (_value == null || _value.Length == 0) return EmptyValueString;
			return String.Join("-", GetByteStrings(null));
		}

		public string ToString(string format)
		{
			return ToString(format, null);
		}

		#region IFormattable Members

		string IFormattable.ToString(string format, IFormatProvider formatProvider)
		{
			return ToString(format, formatProvider);
		}

		#endregion

		public static HashCode Parse(string hashCode)
		{
			return new HashCode(hashCode);
		}
	}
}
